<?php
/**
 * @author spenceryg
 * @date 2022/11/29
 * @time 11:23 下午
 * @focus love & life
 */


namespace app\auto\lib;


use app\auto\business\RelationBusiness;
use app\auto\model\Field;
use app\auto\model\Relation;
use app\auto\model\Table;
use Laminas\Code\Generator\ClassGenerator;
use Laminas\Code\Generator\FileGenerator;
use Laminas\Code\Generator\MethodGenerator;
use Laminas\Code\Generator\PropertyGenerator;
use think\helper\Str;
use think\Model;

class ModelGenerator
{
    public static function generate()
    {
        $tables = Table::select();
        foreach ($tables as $table) {
            self::generateFile($table);
        }
    }


    public static function delete()
    {
        $filename = parent::$config['model'] . static::tableNameToFileName(parent::$tableName) . '.php';
        unlink($filename);
    }


    public static function generateFile($table)
    {
        $class_name = Str::studly(explode('_', $table['name'], 2)[1]);
        $path = '../app/common/model/';
        $name_space = "app\\common\\model";
        $generator = new ClassGenerator(
            $class_name,
            $name_space
        );
        $generator->setExtendedClass(\think\Model::class);
        $generator->addProperty('table', $table['name'], PropertyGenerator::FLAG_PROTECTED);

        $generator->addProperty('jsonAssoc', true, PropertyGenerator::FLAG_PROTECTED);

        if ($table->is_auto_time) {
            $generator->addProperty('autoWriteTimestamp', true, PropertyGenerator::FLAG_PROTECTED);
        }

        if ($table->is_soft_delete) {
            $generator->addUse("think\model\concern\SoftDelete");
            $generator->addTrait('SoftDelete');
        }


        $readonly = [];
        $json = [];
        $schema = [];
        $getters = [];

        $fields = Field::where('table_id', $table['id'])->select();
        foreach ($fields as $filed) {
            $schema[$filed->name] = $filed->type;
            if($filed->type == 'json'){
                $json[] = $filed->name;
            }
        }

        $generator->addProperty('schema', $schema, PropertyGenerator::FLAG_PROTECTED);
        if ($json) {
            $generator->addProperty('json', $json, PropertyGenerator::FLAG_PROTECTED);
        }
        if ($readonly) {
            $generator->addProperty('readonly', $readonly, PropertyGenerator::FLAG_PROTECTED);
        }

        $generator->addProperty('getters', $getters, PropertyGenerator::FLAG_PUBLIC);


        //关联方法
        $relations = Relation::where('table_id_master', '=', $table['id'])->select();
        foreach ($relations as $relation) {
            switch ($relation['type']) {
                case 'belongsToMany':
                    // 增加中间模型
                    $master = Table::find($relation['table_id_master']);
                    $sliver = Table::find($relation['table_id_sliver']);
                    $table_name = $master['name'] . '_' . str_replace(env('DATABASE.PREFIX'), '', $sliver['name']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $table_name));
                    $relation_class_name_to_many = Str::studly($relation_name);

                    $generator_to_many = new ClassGenerator(
                        $relation_class_name_to_many,
                        $name_space
                    );
                    $generator_to_many->addUse("think\\model\\Pivot");
                    $generator_to_many->setExtendedClass("think\\model\\Pivot");
                    $generator_to_many->addProperty('table', $table_name, PropertyGenerator::FLAG_PROTECTED);
                    $filename_to_many = $path . $relation_class_name_to_many . '.php';
                    $content_to_many = (new FileGenerator())->setBody($generator_to_many->generate())->generate();
                    file_put_contents($filename_to_many, $content_to_many);

                    // 处理主表
                    $sliver_table = Table::find($relation['table_id_sliver']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $sliver_table['name']));
                    $relation_class_name = Str::studly($relation_name);
                    $relation_type = $relation['type'];
                    $relation_content = <<<EOF
return \$this->{$relation_type}({$relation_class_name}::class,{$relation_class_name_to_many}::class );
EOF;
                    $generator->addMethod($relation_name, [], MethodGenerator::FLAG_PUBLIC, $relation_content, '关联模型' . $relation_name);

                    break;
                case 'hasOne':
                case 'hasMany':
                case 'morphMany':
                    $sliver_table = Table::find($relation['table_id_sliver']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $sliver_table['name']));
                    $relation_class_name = Str::studly($relation_name);
                    $relation_type = $relation['type'];
                    $relation_content = <<<EOF
return \$this->{$relation_type}({$relation_class_name}::class );
EOF;
                    $generator->addMethod($relation_name, [], MethodGenerator::FLAG_PUBLIC, $relation_content, '关联模型' . $relation_name);
                    break;
                case 'hasOneThrough':
                case 'hasManyThrough':
                    $sliver_table = Table::find($relation['table_id_sliver']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $sliver_table['name']));
                    $relation_class_name = Str::studly($relation_name);
                    $relation_type = $relation['type'];
                    $middle_table = Table::find($relation['table_id_middle']);
                    $middle_relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $middle_table['name']));
                    $middle_class_name = Str::studly($middle_relation_name);
                    $relation_content = <<<EOF
return \$this->{$relation_type}({$relation_class_name}::class , {$middle_class_name}::class);
EOF;
                    $generator->addMethod($relation_name, [], MethodGenerator::FLAG_PUBLIC, $relation_content, '关联模型' . $relation_name);
                    break;
                default:
                    break;
            }

        }

        // 反相关联方法
        $relations_reverse = Relation::where('table_id_sliver', '=', $table['id'])->select();
        foreach ($relations_reverse as $relation) {
            switch ($relation['type']) {
                case 'belongsToMany':
                    $master = Table::find($relation['table_id_master']);
                    $sliver = Table::find($relation['table_id_sliver']);
                    $table_name = $master['name'] . '_' . str_replace(env('DATABASE.PREFIX'), '', $sliver['name']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $table_name));
                    $relation_class_name_to_many = Str::studly($relation_name);

                    $master_table = Table::find($relation['table_id_master']);
                    $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $master_table['name']));
                    $relation_class_name = Str::studly($relation_name);
                    $relation_type = RelationBusiness::$type_relations[$relation['type']];
                    $relation_content = <<<EOF
return \$this->{$relation_type}({$relation_class_name}::class ,{$relation_class_name_to_many}::class);
EOF;
                    $generator->addMethod($relation_name, [], MethodGenerator::FLAG_PUBLIC, $relation_content, '关联模型' . $relation_name);
                    break;
                case 'hasOne':
                case 'hasMany':
                $master_table = Table::find($relation['table_id_master']);
                $relation_name = Str::camel(str_replace(env('DATABASE.PREFIX'), '', $master_table['name']));
                $relation_class_name = Str::studly($relation_name);
                $relation_type = RelationBusiness::$type_relations[$relation['type']];
                $relation_content = <<<EOF
return \$this->{$relation_type}({$relation_class_name}::class );
EOF;
                $generator->addMethod($relation_name, [], MethodGenerator::FLAG_PUBLIC, $relation_content, '关联模型' . $relation_name);
                break;
                default:
                    break;
            }

        }

        foreach ($fields as $filed) {
            if($filed['config']['type'] == 'date' || $filed['config']['type'] == 'time') {
                if(!$filed['is_sys']){
                    $generator->addMethod('set'. Str::studly($filed['name']).'Attr', ['value', 'data'], MethodGenerator::FLAG_PUBLIC, "return \$value?strtotime(\$value):0;", $filed['comment'].'设置器');
                }
            }
        }


        //模型方法
        $model_events = [
            'after_read',
            'before_insert',
            'after_insert',
            'before_update',
            'after_update',
            'before_write',
            'after_write',
            'before_delete',
            'after_delete',
            'before_restore',
            'after_restore',
        ];

//        foreach ($model_events as $event) {
//            $func_name = 'on' . Str::studly($event);
//            $generator->addMethod($func_name, ['model'], MethodGenerator::FLAG_STATIC, '', '模型事件' . $func_name);
//        }


        $filename = $path . $class_name . '.php';
        $content = (new FileGenerator())->setBody($generator->generate())->generate();

        file_put_contents($filename, $content);
    }

}
